package gziprsaes

import (
	"bytes"
	"compress/gzip"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"time"
)

const (
	ivDefValue = "0102030405060708"
)

// rsa加密
func RsaEncrypt(origData, publicKey []byte) ([]byte, error) {
	block, _ := pem.Decode(publicKey) //将密钥解析成公钥实例
	if block == nil {
		return nil, errors.New("public key error")
	}
	pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) //解析pem.Decode（）返回的Block指针实例
	if err != nil {
		return nil, err
	}
	pub := pubInterface.(*rsa.PublicKey)
	return rsa.EncryptPKCS1v15(rand.Reader, pub, origData) //RSA算法加密
}

// rsa解密
func RsaDecrypt(ciphertext, privateKey []byte) ([]byte, error) {
	block, _ := pem.Decode(privateKey) //将密钥解析成私钥实例
	if block == nil {
		return nil, errors.New("private key error!")
	}
	priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) //解析pem.Decode（）返回的Block指针实例
	if err != nil {
		return nil, err
	}
	return rsa.DecryptPKCS1v15(rand.Reader, priv, ciphertext) //RSA算法解密
}

//aes 加密
func AesEncrypt(plaintext, aeskey []byte) ([]byte, error) {
	block, err := aes.NewCipher(aeskey)
	if err != nil {
		return nil, errors.New("invalid decrypt key")
	}
	blockSize := block.BlockSize()
	plaintext = PKCS5Padding(plaintext, blockSize)
	iv := []byte(ivDefValue)
	blockMode := cipher.NewCBCEncrypter(block, iv)

	ciphertext := make([]byte, len(plaintext))
	blockMode.CryptBlocks(ciphertext, plaintext)

	return ciphertext, nil
}

//aes解密
func AesDecrypt(ciphertext, aeskey []byte) ([]byte, error) {

	block, err := aes.NewCipher(aeskey)
	if err != nil {
		return nil, errors.New("invalid decrypt key")
	}

	blockSize := block.BlockSize()

	if len(ciphertext) < blockSize {
		return nil, errors.New("ciphertext too short")
	}

	iv := []byte(ivDefValue)
	if len(ciphertext)%blockSize != 0 {
		return nil, errors.New("ciphertext is not a multiple of the block size")
	}

	blockModel := cipher.NewCBCDecrypter(block, iv)

	plaintext := make([]byte, len(ciphertext))
	blockModel.CryptBlocks(plaintext, ciphertext)
	plaintext = PKCS5UnPadding(plaintext)

	return plaintext, nil
}

//填充
func PKCS5Padding(src []byte, blockSize int) []byte {
	padding := blockSize - len(src)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(src, padtext...)
}

//去填充
func PKCS5UnPadding(src []byte) []byte {
	length := len(src)
	unpadding := int(src[length-1])
	return src[:(length - unpadding)]
}

//BytesCombine 多个[]byte数组合并成一个[]byte
func BytesCombine(pBytes ...[]byte) []byte {
	return bytes.Join(pBytes, []byte("----xbyun.com----"))
}

//压缩加密
func CompresswithCipher(filepath string, publickey, aes_key []byte) {
	t1 := time.Now()
	//目标文件
	bakfile, err := os.Open(filepath)

	var gw_buf bytes.Buffer
	gw := gzip.NewWriter(&gw_buf)
	defer gw.Close()
	if err != nil {
		fmt.Println(err)
	}
	if _, err := io.Copy(gw, bakfile); err != nil {
		fmt.Println(err)
	}
	gw.Flush()

	cipher_data, err := AesEncrypt(gw_buf.Bytes(), aes_key)
	if err != nil {
		fmt.Println(err)
	}

	//rsa对aes_key进行加密
	cipher_key, err := RsaEncrypt(aes_key, publickey)
	if err != nil {
		fmt.Println(err)
	}

	final_data := BytesCombine(cipher_key, cipher_data)
	gz_file, _ := os.Create(time.Now().Format("150405") + ".xbyun.bak")

	defer gz_file.Close()
	if _, err := gz_file.Write(final_data); err != nil {
		fmt.Println(err)
	}

	fmt.Println("文件创建城功....")
	elapsed := time.Since(t1)
	fmt.Println("耗时: ", elapsed)
}

//解密解压
func UnCompresswithCipher(filepath string, privatekey []byte) {
	t1 := time.Now() // get current time
	//获取压缩文件数据
	cipher_file, err := os.Open(filepath)
	if err != nil {
		fmt.Println(err)
	} else {
		println("open file success!")
	}
	cipher_full_data, _ := ioutil.ReadAll(cipher_file)
	cipher_data := bytes.Split(cipher_full_data, []byte("----xbyun.com----"))
	aes_key, _ := RsaDecrypt(cipher_data[0], privatekey)

	data, _ := AesDecrypt(cipher_data[1], aes_key)
	var data_buf bytes.Buffer
	data_buf.Write(data)

	gr, _ := gzip.NewReader(&data_buf)
	defer gr.Close()
	final_data, _ := ioutil.ReadAll(gr)
	final_file, _ := os.Create("primary_data_" + time.Now().Format("150405") + ".sql")
	defer final_file.Close()
	if _, err := final_file.Write(final_data); err != nil {
		fmt.Println(err)
	}

	elapsed := time.Since(t1)
	fmt.Println("共耗时: ", elapsed)
}

//func main() {
// get current time

//读取私钥数据
//	prifile, err := os.Open("private.pem")
//	defer prifile.Close()
//	if err != nil {
//		fmt.Println(err)
//	}
//	prifileinfo, err := prifile.Stat()
//	if err != nil {
//		fmt.Println(err)
//	}
//	privatekey := make([]byte, prifileinfo.Size())
//	_, err = prifile.Read(privatekey)
//	if err != nil {
//		fmt.Println(err)
//	}
//	//读取公钥数据
//	pubfile, err := os.Open("public.pem")
//	defer pubfile.Close()
//	if err != nil {
//		fmt.Println(err)
//	}
//	pubfileinfo, err := pubfile.Stat()
//	if err != nil {
//		fmt.Println(err)
//	}
//	publickey := make([]byte, pubfileinfo.Size())

//	_, err = pubfile.Read(publickey)
//	if err != nil {
//		fmt.Println(err)
//	}

//	publicKey = publickey
//	privateKey = privatekey

//目标文件
//	bakfile, err := os.Open(filepath)

//	var gw_buf bytes.Buffer
//	gw := gzip.NewWriter(&gw_buf)
//	defer gw.Close()
//	if err != nil {
//		fmt.Println(err)
//	}
//	if _, err := io.Copy(gw, bakfile); err != nil {
//		fmt.Println(err)
//	}
//	gw.Flush()

//	cipher_data, err := AesEncrypt(gw_buf.Bytes(), aes_key)
//	if err != nil {
//		fmt.Println(err)
//	}

//	//rsa对aes_key进行加密
//	cipher_key, err := RsaEncrypt(aes_key,publickey)
//	if err != nil {
//		fmt.Println(err)
//	}

//	final_data := BytesCombine(cipher_key, cipher_data)
//	gz_file, _ := os.Create(time.Now().Format("150405") + ".xbyun.bak")

//	defer gz_file.Close()
//	if _, err := gz_file.Write(final_data); err != nil {
//		fmt.Println(err)
//	}

//	fmt.Println("文件创建城功....")
//	elapsed := time.Since(t1)
//	fmt.Println("App elapsed: ", elapsed)
//}
